package transform;

import java.util.Iterator;

import graph.*;

/**
 * An implementation of a transformations which runs the Constant Propagation algorithm
 * @author Habib
 *
 */

public class ConstantPropagationTransformer implements Transformer {

	Graph graph;
	Graph graph_t;
	
	/**
	 * Create an instance of the instransformation without specifying the graph to transform
	 */
	public ConstantPropagationTransformer()
	{
		graph = null;
		graph_t = null;
	}
	
	/**
	 * Create an instance of the transformation with the graph to transform
	 * @param g
	 */
	public ConstantPropagationTransformer(Graph g)
	{
		graph = g.clone();
		graph_t = null;
	}
	
	@Override
	public Graph getGraph() {
		return graph_t;
	}

	/**
	 * Not implemented
	 */
	@Override
	public Graph getInverseCounterExample() {
		return null;
	}

	/**
	 * Not implemented
	 */
	@Override
	public boolean runInverseTransform() {

		return false;
	}

	/**
	 * Not implemented
	 */
	@Override
	public boolean setCounterExample(Graph g) {

		return false;
	}

	@Override
	public boolean setGraph(Graph g) {

		graph = g.clone();
		return true;
	}
	
	@Override
	public boolean runTransform() {
		graph_t = graph.clone();
		
		runTransform2(graph_t.getRoot());
		runTransform2(graph_t.getProperty());
		
		return true;
	}
	
	/**
	 * Helper method
	 * 
	 * @param node
	 */
	private static void runTransform2(Node node)
	{
		if(node instanceof AssignNode)
		{
			/*
			 * If it is an Assignment node, check if the valueNode has a value
			 * and assign it to the variable node
			 */
			AssignNode aN = (AssignNode)node;
			ValueNode valN = aN.getValueNode();
			runTransform2(valN);
			
			if(valN.isValidValue())
			{
				VariableNode varN = aN.getVariableNode();
				if(varN instanceof IntVariableNode)
				{
					((IntVariableNode)varN).setValue(
							((IntValueNode)valN).getValue());
				}
				else if(varN instanceof BoolVariableNode)
				{
					((BoolVariableNode)varN).setValue(
							((BoolValueNode)valN).getValue());		
				}
			}
			
		}
		else if(node instanceof OperatorNode)
		{
			OperatorNode oN = (OperatorNode)node;
			ValueNode leftN = oN.getLeftOperand();
			ValueNode rightN = oN.getRightOperand();
			runTransform2(leftN);
			runTransform2(rightN);
			
			if((leftN.isValidValue())&&(rightN.isValidValue()))
			{
				if(oN.getOperator().equals("+"))
				{
					int result = ((IntValueNode)leftN).getValue() 
									+ ((IntValueNode)rightN).getValue();
					((IntValueNode)oN).setValue(result);
					
				}
				else if(oN.getOperator().equals("-"))
				{
					int result = ((IntValueNode)leftN).getValue() 
								- ((IntValueNode)rightN).getValue();
					((IntValueNode)oN).setValue(result);
				}
				else if(oN.getOperator().equals("*"))
				{
					int result = ((IntValueNode)leftN).getValue() 
								* ((IntValueNode)rightN).getValue();
					((IntValueNode)oN).setValue(result);
				}
				else if(oN.getOperator().equals("/"))
				{
					int result = ((IntValueNode)leftN).getValue() 
								/ ((IntValueNode)rightN).getValue();
					((IntValueNode)oN).setValue(result);
				}
				else if(oN.getOperator().equals("%"))
				{
					int result = ((IntValueNode)leftN).getValue() 
								% ((IntValueNode)rightN).getValue();
					((IntValueNode)oN).setValue(result);
				}
				else if(oN.getOperator().equals("=="))
				{
					if(leftN instanceof IntValueNode)
					{
						boolean result = ((IntValueNode)leftN).getValue() 
									== ((IntValueNode)rightN).getValue();
						((BoolValueNode)oN).setValue(result);
					}
					else
					{
						boolean result = ((BoolValueNode)leftN).getValue() 
									== ((BoolValueNode)rightN).getValue();
						((BoolValueNode)oN).setValue(result);
					}
				}
				else if(oN.getOperator().equals("!="))
				{
					if(leftN instanceof IntValueNode)
					{
						boolean result = ((IntValueNode)leftN).getValue() 
									!= ((IntValueNode)rightN).getValue();
						((BoolValueNode)oN).setValue(result);
					}
					else
					{
						boolean result = ((BoolValueNode)leftN).getValue() 
									!= ((BoolValueNode)rightN).getValue();
						((BoolValueNode)oN).setValue(result);
					}
				}
				else if(oN.getOperator().equals("<"))
				{
					boolean result = ((IntValueNode)leftN).getValue() 
								< ((IntValueNode)rightN).getValue();
					((BoolValueNode)oN).setValue(result);
				}
				else if(oN.getOperator().equals(">"))
				{
					boolean result = ((IntValueNode)leftN).getValue() 
								> ((IntValueNode)rightN).getValue();
					((BoolValueNode)oN).setValue(result);
				}
				else if(oN.getOperator().equals("&&"))
				{
					if(leftN instanceof IntValueNode)
					{
						int result = ((IntValueNode)leftN).getValue() 
									& ((IntValueNode)rightN).getValue();
						((IntValueNode)oN).setValue(result);
					}
					else
					{
						boolean result = ((BoolValueNode)leftN).getValue() 
									&& ((BoolValueNode)rightN).getValue();
						((BoolValueNode)oN).setValue(result);
					}
				}
				else if(oN.getOperator().equals("||"))
				{
					if(leftN instanceof IntValueNode)
					{
						int result = ((IntValueNode)leftN).getValue() 
									| ((IntValueNode)rightN).getValue();
						((IntValueNode)oN).setValue(result);
					}
					else
					{
						boolean result = ((BoolValueNode)leftN).getValue() 
									|| ((BoolValueNode)rightN).getValue();
						((BoolValueNode)oN).setValue(result);
					}
				}
				else if(oN.getOperator().equals("x|"))
				{
					if(leftN instanceof IntValueNode)
					{
						int result = ((IntValueNode)leftN).getValue() 
									^ ((IntValueNode)rightN).getValue();
						((IntValueNode)oN).setValue(result);
					}
					else
					{
						boolean result = ((BoolValueNode)leftN).getValue() 
									^ ((BoolValueNode)rightN).getValue();
						((BoolValueNode)oN).setValue(result);
					}
				}
				
			}
			
		}
		else if(node instanceof IfNode)
		{
			IfNode ifN = (IfNode)node;
			BoolValueNode condN = ifN.getConditionNode();
			runTransform2((Node) condN);
			if(((ValueNode)condN).isValidValue())
			{
				if(condN.getValue()==true)
					ifN.setElseNode(null);
				else
					ifN.setThenNode(null);
			}
			
			BlockNode thenN = ifN.getThenNode();
			BlockNode elseN = ifN.getElseNode();
			
			if(thenN!=null)
				runTransform2(thenN);
			if(elseN!=null)
				runTransform2(elseN);
		}
		else if(node instanceof BlockNode)
		{
			Iterator<Node> it = ((BlockNode)node).iterator();
			while(it.hasNext())
				runTransform2(it.next());
		}
		
	}

}
